/* Copyright (c) 2024 Qualcomm Technologies International, Ltd. */
/* %%version */

#include "csr_bt_cm_lib.h"
#include "l2cap_prim.h"
#include "gatt_ots_client_init.h"
#include "gatt_ots_client_common_util.h"
#include "gatt_ots_client_read_object.h"
#include "gatt_ots_client_abort.h"
#include "gatt_ots_client_con.h"
#include "gatt_ots_client_streams.h"
#ifdef CSR_STREAMS_ENABLE
#include "csr_streams.h"
#endif

#define OTSC_LECOC_DEFAULT_CONN_FLAGS               ((CsrUint16) 0x08)

/*******************************************************************************
* API to open object transfer channel with OTS server device
*******************************************************************************/
bool GattOtscOpenOtsChannelReq(ServiceHandle svcHandle)
{
    GOTSC *gattOtsClient = ServiceHandleGetInstanceData(svcHandle);

    GATT_OTS_CLIENT_INFO("GOTSC: GattOtscOpenOtsChannelReq: svcHandle[%0x]\n", svcHandle);

    if (gattOtsClient)
    {
        if (gattOtsClient->btConnId == CSR_BT_CONN_ID_INVALID)
        {
            OtscInternalMsgOpenOrCloseOtsChannel *message = CsrPmemZalloc(sizeof(*message));

            message->svcHandle = svcHandle;
            GattOtscMessageSend(gattOtsClient->libTask,
                                OTSC_INTERNAL_MSG_OPEN_OTS_CHANNEL_REQ,
                                message);
            return (TRUE);
        }
        else
        {
            GATT_OTS_CLIENT_INFO("GOTSC: OTS Channel is already open!\n");
        }
        return (FALSE);
    }
    else
    {
        GATT_OTS_CLIENT_ERROR("GOTSC: No OTS Client instance found!\n");
        return (FALSE);
    }
}

/*******************************************************************************
* API to close object transfer channel with OTS server device
*******************************************************************************/
bool GattOtscCloseOtsChannelReq(ServiceHandle svcHandle)
{
    GOTSC *gattOtsClient = ServiceHandleGetInstanceData(svcHandle);

    GATT_OTS_CLIENT_INFO("GOTSC: GattOtscCloseOtsChannelReq: svcHandle[%0x]\n", svcHandle);

    if (gattOtsClient)
    {
        if (gattOtsClient->btConnId != CSR_BT_CONN_ID_INVALID)
        {
            OtscInternalMsgOpenOrCloseOtsChannel *message = CsrPmemZalloc(sizeof(*message));

            message->svcHandle = svcHandle;
            GattOtscMessageSend(gattOtsClient->libTask,
                                OTSC_INTERNAL_MSG_CLOSE_OTS_CHANNEL_REQ,
                                message);
            return (TRUE);
        }
        else
        {
            GATT_OTS_CLIENT_INFO("GOTSC: No OTS Channel is open !\n");
        }
        return (FALSE);
    }
    else
    {
        GATT_OTS_CLIENT_ERROR("GOTSC: No OTS Client instance found!\n");
        return (FALSE);
    }
}

void OtscHandleInternalMsgOpenOrCloseOtsChannel(GOTSC *const gattOtsClient, MsgId id)
{
    if (id == OTSC_INTERNAL_MSG_OPEN_OTS_CHANNEL_REQ)
    {
        /* Open OTS L2cap connection */
        if (!OtscInitiateOtsConnection(gattOtsClient))
        {
            GATT_OTS_CLIENT_ERROR("GOTSC: Connection attempt failed !");
            OtscSendOtsChannelOpenedCfm(gattOtsClient,
                                        CSR_BT_CONN_ID_INVALID,
                                        GATT_OTSC_RESULT_CODE_OPERATION_FAILED,
                                        GATT_SUPPLIER_OTS_CLIENT);
        }
    }
    else
    {
        /* Disconnect the OTS channel */
        CsrBtCml2caDisconnectReqSend(gattOtsClient->btConnId);
    }
}

bool OtscRegisterOtsL2capChannel(GOTSC *const gattOtsClient)
{
    CsrBtTypedAddr addrT;

    if (CsrBtGattClientUtilFindAddrByConnId(gattOtsClient->srvcElem->cid,
                                            &addrT))
    {
        CsrUint16 modeMask;

        if (GattClientUtilFindTransportTypeByCid(gattOtsClient->srvcElem->cid))
        {
            modeMask = L2CA_REGFLAG_USE_TP_PRIMS | L2CA_REGFLAG_FOR_LE_TP;
        }
        else
        {
            modeMask = L2CA_REGFLAG_USE_TP_PRIMS | L2CA_REGFLAG_USE_ECBFC;
        }

        CsrBtCmContextl2caRegisterReqSend(gattOtsClient->libTask, OTS_PSM, 0,
                                          modeMask,
                                          gattOtsClient->srvcElem->service_handle);

        return (TRUE);
    }
    else
    {
        GATT_OTS_CLIENT_ERROR("GOTSC: L2cap registration failed !\n");
        return (FALSE);
    }
}

bool OtscInitiateOtsConnection(GOTSC *const gattOtsClient)
{
    CsrBtTypedAddr addrT;

    if (CsrBtGattClientUtilFindAddrByConnId(gattOtsClient->srvcElem->cid,
                                            &addrT))
    {
        CsrBtTpdAddrT tpdAddrT;

        tpdAddrT.addrt = addrT;

        if (GattClientUtilFindTransportTypeByCid(gattOtsClient->srvcElem->cid))
        {
            tpdAddrT.tp_type = LE_ACL;
        }
        else
        {
            tpdAddrT.tp_type = BREDR_ACL;
        }

        CmL2caTpConnectReqExtSend(gattOtsClient->libTask,
                                  tpdAddrT,
                                  OTS_PSM,
                                  OTS_PSM,
                                  OTS_DEAFULT_MTU_SIZE,
                                  SEC_LE_NONE,
                                  CSR_BT_SC_DEFAULT_ENC_KEY_SIZE,
                                  OTS_DEAFULT_INITIAL_CREDITS,
                                  OTSC_LECOC_DEFAULT_CONN_FLAGS,
                                  gattOtsClient->srvcElem->service_handle);
        GATT_OTS_CLIENT_INFO("GOTSC : OTS connection initiated ! \n");
        return (TRUE);
    }
    else
    {
        GATT_OTS_CLIENT_ERROR("GOTSC: Cound not initiate OTS connection !\n");
        return (FALSE);
    }
}

void OtscHandleCmL2capRegisterCfm(GOTSC *const gattOtsClient,
                                  CsrBtCmL2caRegisterCfm *cfm)
{
    /* OTS does l2cap registration for each time new instance is initialized.
     * If the PSM is already registered, L2CAP returns the CFM message with
     * Already Registered result code.
     */
    if ((cfm->resultCode == CSR_BT_RESULT_CODE_CM_SUCCESS) ||
        (cfm->resultCode == L2CA_MISC_ALREADY_REGISTERED &&
         cfm->resultSupplier == CSR_BT_SUPPLIER_L2CAP_MISC))
    {
        /* Nothing left for Init procedures; send the Init CFM to app */
        GattOtscSendInitCfm(gattOtsClient,
                            GATT_OTSC_RESULT_CODE_SUCCESS,
                            GATT_SUPPLIER_OTS_CLIENT);
    }
    else
    {
        GattOtscSendInitCfm(gattOtsClient,
                            GATT_OTSC_RESULT_CODE_OPERATION_FAILED,
                            GATT_SUPPLIER_OTS_CLIENT);
    }
}

void OtscHandleCmL2capConnectCfm(GOTSC *const gattOtsClient,
                                 CsrBtCmL2caTpConnectCfm *cfm)
{
    GATT_OTS_CLIENT_INFO("GOTSC: Handling L2cap Connect Cfm : svcHandle(0x%02x) result(0x%04x) supplier(0x%04x) btConnId(0x%04x)\n",
        cfm->context, cfm->resultCode, cfm->resultSupplier, cfm->btConnId);

    if (cfm->resultCode     == CSR_BT_RESULT_CODE_CM_SUCCESS &&
        cfm->resultSupplier == CSR_BT_SUPPLIER_CM)
    {
        gattOtsClient->btConnId = cfm->btConnId;

#ifdef CSR_STREAMS_ENABLE
        OtscStreamsRegister(cfm);
#endif

        OtscHandleOtsConnectCfm(gattOtsClient);
    }
    else
    {
        if (gattOtsClient->pendingCmd & OTSC_PENDING_OP_READ_OBJ_CONTENT)
        {
            OtscSendReadObjectCfm(gattOtsClient, 0, NULL, FALSE,
                                  cfm->resultCode,
                                  cfm->resultSupplier);
        }
        else
        {
            /*App initiated connection, just send the CFM message back to app */
            OtscSendOtsChannelOpenedCfm(gattOtsClient,
                                        CSR_BT_CONN_ID_INVALID,
                                        cfm->resultCode,
                                        cfm->resultSupplier);
        }
    }
}

void OtscHandleCmL2capDisconnectInd(GOTSC *const gattOtsClient,
                                    CsrBtCmL2caDisconnectInd *ind)
{
    GATT_OTS_CLIENT_INFO("GOTSC: Handling L2cap disconnect IND : svcHandle(0x%02x) reason(0x%04x) supplier(0x%04x) btConnId(0x%04x) \n",
        ind->context, ind->reasonCode, ind->reasonSupplier, ind->btConnId);

#ifdef CSR_BT_INSTALL_CM_DISABLE_AUTO_DISC_RESP
    if (!ind->localTerminated)
    {
        CsrBtCmL2caDisconnectRspSend(ind->l2caSignalId, ind->btConnId);
    }
#endif

    /* Inform app about read failure in mid of reading an object's content */
    if (gattOtsClient->pendingCmd & OTSC_PENDING_OP_READ_OBJ_CONTENT)
    {
        gattOtsClient->btConnId = CSR_BT_CONN_ID_INVALID;

        OtscSendReadObjectCfm(gattOtsClient, 0, NULL, FALSE,
                              GATT_OTSC_RESULT_CODE_TIMEOUT,
                              GATT_SUPPLIER_OTS_CLIENT);
    }
    else
    {
        CsrBtResultCode result;
        CsrBtSupplier   supplier;

        if (ind->localTerminated)
        {
            result   = GATT_OTSC_RESULT_CODE_SUCCESS;
            supplier = GATT_SUPPLIER_OTS_CLIENT;
        }
        else
        {
            result   = ind->reasonCode;
            supplier = ind->reasonSupplier;
        }

        OtscSendOtsChannelClosedInd(gattOtsClient, ind->btConnId,
                                    ind->localTerminated, result, supplier);

        gattOtsClient->btConnId = CSR_BT_CONN_ID_INVALID;
    }

#ifdef CSR_STREAMS_ENABLE
    CsrStreamsUnregister(ind->btConnId, L2CAP_ID);
#endif /* CSR_STREAMS_ENABLE */
}

void OtscSendOtsChannelOpenedCfm(GOTSC *const              gattOtsClient,
                                 CsrBtConnId               btConnId,
                                 CsrBtResultCode           result,
                                 CsrBtSupplier             supplier)
{
    GattOtscOtsChannelOpenedCfm *message = CsrPmemZalloc(sizeof(*message));

    message->svcHandle  = gattOtsClient->srvcElem->service_handle;
    message->btConnId   = btConnId;
    message->resultCode = result;
    message->supplier   = supplier;

    GATT_OTS_CLIENT_INFO("GOTSC: OtscSendOtsChannelOpenedCfm: svcHandle[%0x] btConnId[0x%04x]\n",
                         gattOtsClient->srvcElem->service_handle, btConnId);

    /* Send the confirmation message to app task  */
    GattOtscMessageSend(gattOtsClient->appTask,
                        GATT_OTSC_OTS_CHANNEL_OPENED_CFM,
                        message);
}

void OtscSendOtsChannelClosedInd(GOTSC *const             gattOtsClient,
                                 CsrBtConnId              btConnId,
                                 CsrBool                  locallyTerminated,
                                 CsrBtResultCode          result,
                                 CsrBtSupplier            supplier)
{
    GattOtscOtsChannelClosedInd *message = CsrPmemZalloc(sizeof(*message));

    message->svcHandle         = gattOtsClient->srvcElem->service_handle;
    message->btConnId          = btConnId;
    message->locallyTerminated = locallyTerminated;
    message->resultCode        = result;
    message->supplier          = supplier;

    GATT_OTS_CLIENT_INFO("GOTSC: OtscSendOtsChannelClosedInd: svcHandle[%0x] btConnId[0x%04x] locallyTerminated[%d]\n",
                          gattOtsClient->srvcElem->service_handle, btConnId, locallyTerminated);

    /* Send the confirmation message to app task  */
    GattOtscMessageSend(gattOtsClient->appTask,
                        GATT_OTSC_OTS_CHANNEL_CLOSED_IND,
                        message);
}


